package com.xxm.mediacodec

import android.app.Application
import android.graphics.Bitmap
import android.graphics.PixelFormat
import android.graphics.Rect
import android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioTrack
import android.media.MediaCodec
import android.media.MediaCodecList
import android.media.MediaExtractor
import android.media.MediaFormat
import android.util.Log
import android.view.SurfaceHolder
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.xxm.util.AudioPlayer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.text.DecimalFormat

class MediaCodecModel(application: Application) : AndroidViewModel(application) {
    private val TAG = "MediaCodecModel"
    fun supportCodecType() {
        val mediaCodecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
        mediaCodecList.codecInfos.forEach {
            if (it.isEncoder) {
                Log.d(TAG, "编码器：${it.name}")
            } else {
                Log.d(TAG, "解码器：${it.name}")
            }
        }

    }


    private lateinit var mediaExtractor: MediaExtractor
    private lateinit var mediaCodec: MediaCodec

    private var inputIndex = 0

    private var outIndex = 0
    private var audioOutputIndex = 0

    private var job: Job? = null

    private var isPlaying = false


    private val audioExtractor: MediaExtractor = MediaExtractor()
    private  var audioDecoder: MediaCodec?=null
    private  var audioTrack: AudioTrack?=null
    private var audioJob:Job?=null
    fun startAudio(mp4: String) {

        audioJob=viewModelScope.launch(Dispatchers.IO) {
            audioExtractor.setDataSource(mp4)
            var audioChannel = 0
            var audioSample = 0
            var mineType = ""
            var mediaFormat: MediaFormat? = null
            for (i in 0 until audioExtractor.trackCount) {
                mediaFormat = audioExtractor.getTrackFormat(i)
                mineType = mediaFormat.getString(MediaFormat.KEY_MIME) ?: ""
                if (mineType?.startsWith("audio/") == true) {
                    audioExtractor.selectTrack(i)
                    audioSample = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE)
                    audioChannel = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
                    Log.d(TAG, ">>> audio sample:${audioSample},channel:${audioChannel}")
                    break
                }
            }

            val minBufferSize = AudioTrack.getMinBufferSize(audioSample,  if (audioChannel == 1) AudioFormat.CHANNEL_OUT_MONO else AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT)
            Log.d(TAG,"audio track min buffer size:${minBufferSize}")
            audioTrack= AudioTrack(AudioManager.STREAM_MUSIC,audioSample, if (audioChannel == 1) AudioFormat.CHANNEL_OUT_MONO else AudioFormat.CHANNEL_OUT_STEREO,AudioFormat.ENCODING_PCM_16BIT,minBufferSize,AudioTrack.MODE_STREAM)
            audioDecoder = MediaCodec.createDecoderByType(mineType)
            audioDecoder?.configure(mediaFormat, null, null, 0)
            audioDecoder?.start()
            audioTrack?.play()
            val bufferInfo = MediaCodec.BufferInfo()
            var lastTime = System.currentTimeMillis()
            while (isActive) {
                val inputIndex = audioDecoder!!.dequeueInputBuffer(10000)
                if(inputIndex>0){
                    val inputBuffer = audioDecoder!!.getInputBuffer(inputIndex)
                    val sampleSize = audioExtractor.readSampleData(inputBuffer!!, 0)
                    Log.d(TAG, ">>>> sample size:${sampleSize}")
                    if (sampleSize < 0) {
                        Log.d(TAG, ">>>> input buffer is EOS")
                        audioDecoder!!.queueInputBuffer(
                            inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM
                        )
                    } else {
                        Log.d(TAG, ">>>>audio input buffer")
                        audioDecoder!!.queueInputBuffer(
                            inputIndex, 0, sampleSize, audioExtractor.sampleTime, 0
                        )
                        audioExtractor.advance()
                    }
                }

                var outputIndex = audioDecoder!!.dequeueOutputBuffer(bufferInfo, 10000)
                Log.d(TAG, ">>> audio index:${outputIndex}")
                if(outputIndex>=0){
                    val outputBuffer = audioDecoder!!.getOutputBuffer(outputIndex)
                    val chunPCM= ByteArray(bufferInfo.size)
                        outputBuffer?.get(chunPCM)

                    audioTrack!!.write(chunPCM,0,bufferInfo.size)
                    audioDecoder?.releaseOutputBuffer(outputIndex, false)
                }


            }
            audioTrack?.stop()
            audioDecoder?.release()
            audioTrack?.release()
        }


    }

    private var audioPlayer=AudioPlayer()

    fun start(mp4: String, holder: SurfaceHolder) {
        if (isPlaying) return
        isPlaying = true
     //  startAudio(mp4)
        audioPlayer.playMp4(mp4)
        job = viewModelScope.launch(Dispatchers.IO) {
            mediaExtractor = MediaExtractor()
            mediaExtractor.setDataSource(mp4)

            for (i in 0 until mediaExtractor.trackCount) {
                val mediaFormat = mediaExtractor.getTrackFormat(i)
                Log.d(TAG, ">>>>format,index:${i},format:${mediaFormat}")
                val mineType = mediaFormat.getString(MediaFormat.KEY_MIME)
                Log.d(TAG, ">>>>mine,index:${i},mine:${mineType}")
                if (mineType?.startsWith("video/") == true) {
                    Log.d(TAG, ">>>> select:${mineType},${mediaFormat}")
                    mediaExtractor.selectTrack(i)
                    mediaCodec = MediaCodec.createDecoderByType(mineType)
                    mediaCodec.configure(mediaFormat, holder.surface, null, 0)
                    break
                }
            }

            mediaCodec.start()
            val bufferInfo = MediaCodec.BufferInfo()
            var isEos = false
            var startMs = System.currentTimeMillis()
            while (isActive) {
                if (!isEos) {
                    inputIndex = mediaCodec.dequeueInputBuffer(1000)
                    if (inputIndex >= 0) {
                        val inputBuffer = mediaCodec.getInputBuffer(inputIndex)
                        val sampleSize = mediaExtractor.readSampleData(inputBuffer!!, 0)
                        Log.d(TAG, ">>>> sample size:${sampleSize}")
                        if (sampleSize < 0) {
                            Log.d(TAG, ">>>> input buffer is EOS")
                            mediaCodec.queueInputBuffer(
                                inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM
                            )
                            isEos = true
                        } else {
                            mediaCodec.queueInputBuffer(
                                inputIndex, 0, sampleSize, mediaExtractor.sampleTime, 0
                            )
                            mediaExtractor.advance()
                        }
                    }
                }

                var outputIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000)
                when (outputIndex) {
                    MediaCodec.INFO_OUTPUT_FORMAT_CHANGED -> Log.d(TAG, ">>>>INFO_OUTPUT_FORMAT_CHANGED")
                    MediaCodec.INFO_TRY_AGAIN_LATER -> {
                        Log.d(TAG, ">>>>INFO_TRY_AGAIN_LATER")
                    }
                    MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED -> {
                        // val outputBuffer = mediaCodec.getOutputBuffer(outputIndex)
                        Log.d(TAG, ">>>>INFO_OUTPUT_BUFFERS_CHANGED")
                    }
                    else -> {
                        while (bufferInfo.presentationTimeUs / 1000 > System.currentTimeMillis() - startMs) {
                            Thread.sleep(10)
                            Log.d(TAG, "release index:${outputIndex}")
                            try {
                                if (outputIndex != outIndex) {
                                    outIndex = outputIndex
                                    mediaCodec.releaseOutputBuffer(outputIndex, true)
                                }
                            } catch (e: java.lang.Exception) {
                                e.printStackTrace()
                            }
                        }
                    }
                }

                if (bufferInfo.flags.and(MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    Log.d(TAG, ">>>>>>>>>>BUFFER_FLAG_END_OF_STREAM")
                    break
                }
            }
            audioPlayer.release()
            mediaCodec.stop()
            mediaCodec.release()
            mediaExtractor.release()
            isPlaying = false
        }

    }

    fun stop() {
        job?.cancel()
        job = null
        audioJob?.cancel()
        audioJob=null
    }

    fun formatFileSize(fileS: Long): String? {
        val df = DecimalFormat("#.00")
        var fileSizeString = ""
        val wrongSize = "0B"
        if (fileS == 0L) {
            return wrongSize
        }
        fileSizeString = if (fileS < 1024) {
            df.format(fileS.toDouble()) + "B"
        } else if (fileS < 1024 * 1024) {
            df.format(fileS.toDouble() / 1024) + "KB"
        } else if (fileS < 1024 * 1024 * 1024) {
            df.format(fileS.toDouble() / (1024 * 1024)) + "MB"
        } else {
            df.format(fileS.toDouble() / (1024 * 1024 * 1024)) + "GB"
        }
        return fileSizeString
    }

    fun setThumbnail(holder: SurfaceHolder, bitmap: Bitmap, width: Int, height: Int) {

        val canvas = holder.lockCanvas()
        canvas?.drawBitmap(bitmap!!, Rect(0, 0, bitmap.width, bitmap.height), Rect(0, 0, width, height), null)
        canvas?.let {
            holder.unlockCanvasAndPost(it)
        }

        holder.setFormat(PixelFormat.TRANSPARENT);
        holder.setFormat(PixelFormat.OPAQUE);
    }


//    fun createMediaCodec(){
//        val mediaCodec= MediaCodec .createByCodecName(Medi)
//    }
}